#!/usr/bin/env python
"""
Provide Type classes for conversion from types to bytestrings and vice versa.


only implemented 4 classes - please add all classes that you need late later on

S8
S16L
S32L
S16B
S32B

U8
U16L
U32L
U16B
U32B


"""
__author__      = "Michael J. Hohmann"
__copyright__   = "Copyright 2017, Linde Material Handling GmbH"
__version__     = "0.0.1"
__email__       = "michael.hohmann@lind-mh.de"
__status__      = "Prototype"
__date__        = "2017-12-18"

class stdtype:
    pass
    
    
class U8( stdtype ):
    
    def __init__(self, value=None ):
        self.value=value

    def get(self):
        return self.value
         
    def set(self,value):
        self.value=value        
         
    def get_size(self):
        return 1
    
    def is_unsigned(self):
        return True

    def is_signed(self):
        return not self.is_unsigned()
           
    def is_big(self):
        return None

    def is_little(self):
        return None 
       
    def get_as_list(self,value=None):
        if value==None:
            value=self.value
        return [ value & 0xff ]

    def get_as_bytes(self,value=None):
        if value==None:
            value=self.value
        return bytes( (value & 0xff))               
        
    def set_from_list(self,lst,position=0):
        self.value=lst[position+0]
        return self.value

class S8( stdtype ):
    
    def __init__(self, value=None ):
        self.value=int(value)

    def get(self):
        return self.value
         
    def set(self,value):
        self.value=value        
         
    def get_size(self):
        return 1
    
    def is_unsigned(self):
        return False

    def is_signed(self):
        return not self.is_unsigned()
           
    def is_big(self):
        return None

    def is_little(self):
        return None 
       
    def get_as_list(self,value=None):
        if value==None:
            value=self.value
        if self.value>=0:
            return [self.value]       
        return [0x100+value]               

    def get_as_bytes(self,value=None):
        if value==None:
            value=self.value
        if self.value>=0:
            return bytes( self.value)       
        return bytes( 0x100+value)               
        
    def set_from_list(self,lst,position=0):
        value=lst[position+0]
        if value &0x80:
            self.value=-0x100+value
        else:
            self.value=value&0x7F
            
        return self.value


   
class U16L( stdtype ):
    
    def __init__(self, value=None ):
        self.value=value

    def get(self):
        return self.value
         
    def set(self,value):
        self.value=value        
         
    def get_size(self):
        return 2
    
    def is_unsigned(self):
        return True

    def is_signed(self):
        return not self.is_unsigned()
           
    def is_big(self):
        return False

    def is_little(self):
        return not self.is_big() 
       
    def get_as_list(self,value=None):
        if value==None:
            value=self.value
        return [ value & 0xff, (value>>8) & 0xff ]

    def get_as_bytes(self,value=None):
        if value==None:
            value=self.value
        return bytes( (value & 0xff, (value>>8) & 0xff) )     
        
    def set_from_list(self,lst,position=0):
        self.value=(lst[position+1]<<8)+lst[position+0]
        return self.value


class S16L( stdtype ):
    
    def __init__(self, value=None ):
        self.value=value

    def get(self):
        return self.value
         
    def set(self,value):
        self.value=value        
         
    def get_size(self):
        return 2
    
    def is_unsigned(self):
        return False

    def is_signed(self):
        return not self.is_unsigned()
           
    def is_big(self):
        return False

    def is_little(self):
        return not self.is_big() 
       
    def get_as_list(self,value=None):
        if value==None:
            value=self.value
        if value<0:
            data=0x10000+value
        else:
            data=value
        return [ data & 0xff, (data>>8) & 0xff ]

    def get_as_bytes(self,value=None):
        if value==None:
            value=self.value
        if value<0:
            data=0x10000+value
        else:
            data=value
        return bytes( (data & 0xff, (data>>8) & 0xff) )     
        
    def set_from_list(self,lst,position=0):
        data=(lst[position+1]<<8)+lst[position+0]
        if data&0x8000:
            self.value=-0x10000+data
        else:
            self.value=data
        
        return self.value

   
class U32L( stdtype ):
    
    def __init__(self, value=None ):
        self.value = value

    def get(self):
        return self.value
         
    def set(self,value):
        self.value=value        
         
    def get_size(self):
        return 4
    
    def is_unsigned(self):
        return True

    def is_signed(self):
        return not self.is_unsigned()
           
    def is_big(self):
        return False

    def is_little(self):
        return not self.is_big() 
       
    def get_as_list(self,value=None):
        if value==None:
            value=self.value
        return [ value & 0xff, (value>>8) & 0xff, (value>>16) & 0xff, (value>>24) & 0xff ]

    def get_as_bytes(self,value=None):
        if value==None:
            value=self.value
        return bytes( (value & 0xff, (value>>8) & 0xff, (value>>16) & 0xff, (value>>24) & 0xff) )
        
    def set_from_list(self,lst,position=0):
        self.value=(lst[position+3]<<24)+(lst[position+2]<<16)+(lst[position+1]<<8)+lst[position+0]
        return self.value

class U32B( stdtype ):
    
    def __init__(self, value=None ):
        self.value= value

    def get(self):
        return self.value
         
    def set(self,value):
        self.value=value        
         
    def get_size(self):
        return 4
    
    def is_unsigned(self):
        return True

    def is_signed(self):
        return not self.is_unsigned()
           
    def is_big(self):
        return True

    def is_little(self):
        return not self.is_big() 
       
    def get_as_list(self,value=None):
        if value==None:
            value=self.value
        return [ (value>>24) & 0xff,(value>>16) & 0xff,(value>>8) & 0xff, value & 0xff ]

    def get_as_bytes(self,value=None):
        if value==None:
            value=self.value
        return bytes( (value>>24) & 0xff,(value>>16) & 0xff,(value>>8) & 0xff, value & 0xff )
        
    def set_from_list(self,lst,position=0):
        self.value=(lst[position+0] + (lst[position+1]<<8)+ (lst[position+2]<<16) + lst[position+3]<<24)
        return self.value
        
class STR( stdtype ):
    def __init__(self, value=None ):
        if type(value)==str:
            self.value=value
            self.const_string_length=len(self.value)
        elif type(value)==int:
            self.value=None
            self.const_string_length=value
        elif value==None:
            self.value=None
            self.const_string_length=None
        else:
            raise(TypeError)

    def get(self):
        return self.value
         
    def set(self,value):
        self.value=value       
    
    def get_size(self):
        return self.const_string_length
    
    def is_signed(self):
        return None

    def is_unsigned(self):
        return None
        
    def is_little(self):
        return None # Big Endian = True
        
    def is_big(self):
        return None
        
    def get_as_list(self,value=None):
        if value==None:
            value=self.value
        return list(map(ord, self.value))

    def get_as_bytes(self,value=None):
        if value==None:
            value=self.value
        return self.value.encode('ascii')       
        
    def set_from_list(self,lst,position=0 ):
        if self.const_string_length==None:
            self.value= ''.join(map(chr,lst[position:])) # read everything until end
        else:
            self.value= ''.join(map(chr,lst[position:position+self.const_string_length]))
        self.const_string_length=len(self.value)
        return self.value
          
def pack_to_list( args ):
    rv=[]
    for item in args:
        if isinstance(item,stdtype):
            rv+=item.get_as_list()
        else:
            rv+=[item]
    return rv

def pack_to_bytes( args ):
    rv=b''
    for item in args:
        if isinstance(item,stdtype):
            rv+=item.get_as_bytes()
        else:
            rv+=bytes( (item))
    return rv
    
    
    
def unpack( format, msg ):
    pos=0
    rv=[]
    for item in format:
        if type(item) == type(stdtype): # test is weak - assuming is a expected class inherited from stdtype
            xitem=item()
        else:
            xitem=item
        rv+=[xitem.set_from_list(msg,pos)]
        pos+=xitem.get_size()
    if pos!=len(msg):
        raise(TypeError) # not the expected len
    return tuple(rv)
          
          
    
if __name__=="__main__":
    
    test_count=0
    
 

 
    def test( result,text):
        global test_count
        test_count+=1
        if result==True:
            print("OK     "+str(test_count)+"   "+text)
        else:
            print("ERROR  "+str(test_count)+"   "+text)


    
    var_u32l=U32L()
    test( 4     ==var_u32l.get_size(),"U32L size")    
    test( 0x04030201==var_u32l.set_from_list([0x00,0x01,0x02,0x03,0x04,0x05],position=1),"U32L read from list")
    test( [0x01,0x02,0x03,0x04]==var_u32l.get_as_list(),"U32L get as list")
    
    var_u16l=U16L()
    test( 2     ==var_u16l.get_size(),"U16L size")
    test( 0x0201     ==var_u16l.set_from_list([0x00,0x01,0x02,0x03,0x04,0x05],position=1),"U16L read from list")
    test( [0x01,0x02]==var_u16l.get_as_list(),"U16L get as list")

    var_u8=U8()
    test( 1     ==var_u8.get_size(),"U8 size")
    test( 0x01     ==var_u8.set_from_list([0x00,0x01,0x02,0x03,0x04,0x05],position=1),"U8 read from list")
    test( [0x01]==var_u8.get_as_list(),"U8 get as list")

    test_str ="Always Look on the Bright Side of Life"
    test_list=[]
    for char in test_str:
        test_list+=[ord(char)]
    
    var_str=STR(10)
    test( 10     ==var_str.get_size(),"string size")
    test( "Look on th"   ==var_str.set_from_list(test_list,position=7),"STR read from list")
    test( test_list[7:17]==var_str.get_as_list(),"STR get as list")
    print( var_str.get_as_bytes())
   
    test( [1,2,3]==pack_to_list( (1,2,3) ),"build msg easy")
    
    any_msg=pack_to_list( (1,2,3,U32L(0x12345678),U16L(0xAABB),STR("test"), 1,2,3)) 
    print( pack_to_bytes( (1,2,3,U32L(0x12345678),U16L(0xAABB),STR("test"), 1,2,3)))
    any_msg=bytes(any_msg)
    print(str(any_msg))
    any_data=unpack( (U16L,U8,U32L,U16L,STR(4),STR),any_msg )
    print(str(any_data))
    
    
    
""""    pack('<BBHL',service,sid,local)
    send [sid,sys]+msg_decode( ('asx)"]"U32L S16IHLIK",versio,abc),"IUA"
    
    
    (U32L,U16L,STR(10) )
"""        